﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MCWorldLibrary
{
    public class World
    {

        private Dictionary<Vector2, Region> regionCache;
        private string worldFolder;

        public World(string path)
        {
            this.worldFolder = path;
            this.regionCache = new Dictionary<Vector2, Region>(new Vector2.EqualityComparer());
        }

        public void SaveChanges()
        {
            foreach (Region region in this.regionCache.Values)
            {
                region.Save(worldFolder);
            }
        }

        public void SetBiome(BiomeType biome, int x0, int z0, int x1, int z1)
        {

        }

        public void SetBlocks(BlockData[, ,] blocks, int xOffset, int yOffset, int zOffset, bool airOverwrites)
        {
            int xUpperBound = blocks.GetUpperBound(0) + xOffset + 1;
            int yUpperBound = blocks.GetUpperBound(1) + 1;
            int zUpperBound = blocks.GetUpperBound(2) + zOffset + 1;

            Vector3 offset = new Vector3(xOffset, yOffset, zOffset);

            Vector2 originChunkLocation = new Vector2(xOffset / 16, zOffset / 16);
            Vector3 originBlockStart = new Vector3(xOffset - originChunkLocation.X * 16, yOffset, zOffset - originChunkLocation.Y * 16);
            Vector3 originBlockEnd = new Vector3(Math.Min(16 - originBlockStart.X, xUpperBound), yUpperBound + originBlockStart.Y, 16 - originBlockStart.Z);

            Vector3 worldCoordinateStart = new Vector3(originBlockStart);
            Vector3 worldCoordinateEnd = new Vector3(originBlockEnd);
            Vector2 chunkLocation = new Vector2(originChunkLocation);
            for (int x = xOffset; x < xUpperBound; )
            {
                for (int z = zOffset; z < zUpperBound; )
                {
                    this.UpdateChunk(blocks, chunkLocation, worldCoordinateStart, worldCoordinateEnd, offset, airOverwrites);

                    z += 16;
                    worldCoordinateStart.Z = z;
                    worldCoordinateEnd.Z = Math.Min(worldCoordinateStart.Z + 16, zUpperBound - z);
                    chunkLocation.Y++;
                }

                x += 16;
                worldCoordinateStart.X = x;
                worldCoordinateEnd.X = Math.Min(worldCoordinateStart.X + 16, worldCoordinateStart.X + (xUpperBound - x));
                chunkLocation.X++;
                chunkLocation.Y = originChunkLocation.Y;

                worldCoordinateStart.Z = originBlockStart.Z;
                worldCoordinateEnd.Z = originBlockEnd.Z;
            }


        }

        private void UpdateChunk(BlockData[, ,] blocks, Vector2 chunkCoordinate, Vector3 worldCoordinateStart, Vector3 worldCoordinateEnd, Vector3 blocksOffset, bool airOverwrites)
        {
            Vector2 chunkRegionCoordinate = new Vector2(chunkCoordinate.X / 32, chunkCoordinate.Y / 32);
            Region region = LoadRegion(chunkRegionCoordinate);
            region.SetChunkBlocks(blocks, chunkCoordinate, worldCoordinateStart, worldCoordinateEnd, blocksOffset, airOverwrites);
        }

        private Region LoadRegion(Vector2 regionCoordinate)
        {
            Region region;
            if (!regionCache.TryGetValue(regionCoordinate, out region))
            {
                region = new Region(regionCoordinate);
                if (Region.Exists(worldFolder, regionCoordinate.X, regionCoordinate.Y))
                    region.Load(worldFolder, regionCoordinate.X, regionCoordinate.Y);

                regionCache.Add(regionCoordinate, region);
            }

            return region;
        }

        public void RecalculateHeightmaps()
        {
            foreach (Region region in this.regionCache.Values)
            {
                foreach (Chunk chunk in region.chunks.Values)
                {
                    chunk.RecalculateHeightMap();
                }
            }
        }
    }
}
